package com.lhz.service.impl;


import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lhz.config.ServiceException;
import com.lhz.manager.JobManager;
import com.lhz.mapper.JobMapper;
import com.lhz.mapper.JobRegistryMapper;
import com.lhz.model.constant.DataStatusEnum;
import com.lhz.model.constant.TriggerStatusEnum;
import com.lhz.model.entity.JobInfo;
import com.lhz.model.form.JobInfoForm;
import com.lhz.model.param.JobInfoParam;
import com.lhz.model.response.NewPageBean;
import com.lhz.model.vo.JobInfoVo;
import com.lhz.service.ExecutorService;
import com.lhz.service.JobService;
import com.lhz.utils.CronUtils;
import com.lhz.utils.JsonValidatorUtils;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;

/**
 * @Author LiGezZ
 * @Date 2021/08/02 20:57:07
 * @Description: 定时任务调度
 */
@Service
public class JobServiceImpl extends ServiceImpl<JobMapper, JobInfo> implements JobService {

    @Resource
    private JobMapper jobMapper;

    @Resource
    private JobManager jobManager;

    @Resource
    private JobRegistryMapper jobRegistryMapper;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private ExecutorService executorService;

    /**
     * 根据Id查询
     *
     * @param id
     * @return
     */
    @Override
    public Object selectByPrimaryKey(Long id) {
        JobInfo jobInfo = jobMapper.selectById(id);
        JobInfoVo jobInfoVo = new JobInfoVo();
        if (jobInfo != null) {
            BeanUtils.copyProperties(jobInfo, jobInfoVo);
        }
        List<String> nextExecution = (List<String>) CronUtils.getNextExecution(jobInfoVo.getCron(), 5, ZonedDateTime.now(), true);
        jobInfoVo.setNextExecution(nextExecution);
        return jobInfoVo;
    }

    /**
     * 查询定时任务调度列表
     *
     * @param param
     * @return
     */
    @Override
    public Object listInfoPage(JobInfoParam param) {
        //分页
        return NewPageBean.generatePage(() -> jobMapper.listInfoPage(param), param, JobInfo.class);
    }


    /**
     * 新增定时任务调度
     *
     * @param form
     * @return
     */
    @Override
    public int insert(JobInfoForm form) {

        checkExecutorHandler(form);

        checkName(form.getId(), form.getName());
        String cron = form.getCron();
        // 判断corn是否有效
        Assert.isTrue(CronUtils.isValid(cron), "表达式不正确!");
        // 判断json是否有效
        String executorParam = form.getExecutorParam();

        if (!JsonValidatorUtils.isValidJson(executorParam)) {
            throw new ServiceException("参数格式必须为json");
        }

        // 封装数据
        JobInfo jobInfo = new JobInfo();
        BeanUtils.copyProperties(form, jobInfo);
        jobInfo.setStatus(1);
        jobInfo.setDelFlag(1);
        //查询执行周期
        LocalDateTime nextTime = CronUtils.nextCurrentTime(cron);
        jobInfo.setTriggerNextTime(nextTime);
        //设置版本好为0
        jobInfo.setVersion(0);
        jobInfo.setTriggerStatus(TriggerStatusEnum.STOP.getCode());
        //插入数据库
        return jobMapper.insert(jobInfo);
    }

    /**
     * 修改定时任务调度
     *
     * @param form
     * @return
     */
    @Override
    public int updateByPrimaryKey(JobInfoForm form) {
        checkExecutorHandler(form);

        checkName(form.getId(), form.getName());

        String cron = form.getCron();
        // 判断corn是否有效
        Assert.isTrue(CronUtils.isValid(cron), "表达式不正确!");

        // 判断json是否有效
        String executorParam = form.getExecutorParam();
        if (!JsonValidatorUtils.isValidJson(executorParam)) {
            throw new ServiceException("参数格式必须为json");
        }

        // 封装数据
        JobInfo jobInfo = new JobInfo();
        BeanUtils.copyProperties(form, jobInfo);

        JobInfo job = jobMapper.selectById(form.getId());

        // 如果当前任务处于执行中，则重新执行任务
        if (job.getTriggerStatus().equals(TriggerStatusEnum.RUNNING.getCode())) {
            // 任务关键参数被修改
            if (!Objects.equals(jobInfo.getCron(), job.getCron()) || !Objects.equals(jobInfo.getExecutorHandler(), job.getExecutorHandler()) || !Objects.equals(jobInfo.getExecutorParam(), job.getExecutorParam())) {
                job.setCron(jobInfo.getCron());
                job.setExecutorHandler(jobInfo.getExecutorHandler());
                job.setExecutorParam(jobInfo.getExecutorParam());
                try {
                    // 为当前机器
                    jobManager.start(job);
                } finally {
                    // 异步 通知其他机器，http进行请求
                    executorService.startJob(job);
                }
            }
        }
        // 更新数据库
        return jobMapper.updateById(jobInfo);
    }


    /**
     * 删除定时任务调度
     *
     * @param id
     * @return
     */
    @Override
    public int deleteByPrimaryKey(Long id) {

        // 关闭任务
        stopJob(id);

        //数据库删除
        return jobMapper.deleteById(id);
    }

    /**
     * 单次执行任务
     *
     * @param id 任务id
     */
    @Override
    public void invokeJob(Long id) {
        JobInfo jobInfo = getJobInfo(id);
        Integer triggerStatus = jobInfo.getTriggerStatus();
        Assert.isFalse(triggerStatus.equals(TriggerStatusEnum.RUNNING.getCode()), "任务执行中,无法进行单次执行");

        // 单次执行
        jobManager.invoke(jobInfo);
    }

    /**
     * 启动任务
     *
     * @param id 任务id
     */
    @Override
    public void startJob(Long id) {
        JobInfo jobInfo = getJobInfo(id);
        try {
            Integer triggerStatus = jobInfo.getTriggerStatus();
            // Assert.isFalse(triggerStatus.equals(TriggerStatusEnum.RUNNING.getCode()), "任务执行中,无法再次启动");

            // 启动
            jobManager.start(jobInfo);
        } finally {
            // 异步 通知其他机器，http进行请求
            executorService.startJob(jobInfo);
        }

    }

    /**
     * 暂停任务
     *
     * @param id 任务id
     */
    @Override
    public void stopJob(Long id) {
        JobInfo jobInfo = getJobInfo(id);
        try {
            jobManager.stop(jobInfo);
        } finally {
            // 异步 通知其他机器，http进行请求
            executorService.stopJob(jobInfo);
        }

    }

    /**
     * 禁用任务
     *
     * @param id
     */
    @Override
    public void disableJob(Long id) {
        JobInfo jobInfo = getJobInfo(id);
        try {
            // 更新状态
            jobInfo.setStatus(DataStatusEnum.INVALID.getCode());

            // 关闭任务
            jobManager.stop(jobInfo);
            // 更新数据库
            jobMapper.updateById(jobInfo);
        } finally {
            // 异步 通知其他机器，http进行请求
            executorService.stopJob(jobInfo);
        }

    }

    /**
     * 获取Job对象
     *
     * @param id
     * @return
     */
    private JobInfo getJobInfo(Long id) {
        JobInfo jobInfo = jobMapper.selectById(id);
        Assert.notNull(jobInfo, "操作失败，任务不存在");
        return jobInfo;
    }

    /**
     * 检查名称是否重复
     *
     * @param id
     * @param handler
     */
    private void checkName(Long id, String handler) {
        if (StrUtil.isNotBlank(handler)) {
            int size = jobMapper.checkName(id, handler);
            Assert.isFalse(size > 0, "名称重复");
        }
    }

    /**
     * 检查Handler是否重复
     *
     * @param job
     */
    private void checkExecutorHandler(JobInfoForm job) {
        Long jobId = job.getId();
        String jobName = job.getName();
        String invokeTarget = job.getExecutorHandler();
        String executorParam = job.getExecutorParam();
        int size = jobMapper.checkExecutorHandler(jobId, invokeTarget);
        Assert.isFalse(size > 0, "任务Handler不能重复");
        // 验证执行器是否存在
        try {
            jobManager.checkExecutorHandler(jobId, jobName, invokeTarget, executorParam);
        } catch (Exception e) {
            log.error("任务Handler不存在!", e);
            throw new ServiceException("任务Handler不存在!");
        }
    }
}
